from django.shortcuts import render,redirect
from django.http import HttpResponse
from django.views.generic import View
from django.core.urlresolvers import reverse
import re
from users.models import User,Address
from django import db
from django.conf import settings
from celery_tasks.tasks import send_active_email
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
from itsdangerous import SignatureExpired
from django.contrib.auth import authenticate, login,logout
from django.contrib.auth.decorators import login_required
from utils.views import LoginRequiredMixin
from django_redis import get_redis_connection
from goods.models import GoodsSKU
import json
# Create your views here.


class UserInfoView(LoginRequiredMixin,View):
    """个人信息"""
    def get(self,request):
        """查询个人基本信息和最近浏览记录，并渲染模板"""
        #获取user
        user = request.user

        #查询地址信息
        try:
            address = user.address_set.latest('create_time')
        except Address.DoesNotExist:
            address = None

        #查询浏览记录：需要从redis中查询出浏览记录信息
        # 创建连接到redis数据库的对象
        redis_conn = get_redis_connection('default')
        #查询出需要展示的浏览记录数据　sku＿ids = [2,3,4,5,6]
        sku_ids = redis_conn.lrange('history_%s' %user.id,0,4)
        #记录sku模型对象的列表
        sku_list = []
        #遍历　取出sku_id
        for sku_id in sku_ids:
            #使用sku_id查询GoodsSku
            sku = GoodsSKU.objects.get(id = sku_id)
            sku_list.append(sku)

        num = 1
        num = str(num)

        #构造上下文
        context = {
            'address':address,
            'sku_list':sku_list,
            'num':num
        }

        #渲染模板
        return render(request,'user_center_info.html',context)

class AddressView(LoginRequiredMixin,View):
    """收货地址"""

    def get(self,request):
        """查询地址信息，并渲染页面，响应给用户"""
        #获取登录的user
        user = request.user

        #查询该登录用户的地址信息 只展示最新的那一个地址,按照地址创建的时间倒序，并取出第０个
        #address = Address.objects.filter(user=user).order_by('-create_time')[0]
        #address = user.address_set.order_by('-create_time')[0]
        #latest默认倒序，然后取出第０个
        try:
            address = user.address_set.latest('create_time')
        except Address.DoesNotExist:
            #将来在模板中．判断address是否为Ｎｏｎｅ　,此时把地址空出类
            address = None

        #构造上下文
        context = {
            #'request':request,
            'address':address
        }

        #渲染模板
        return render(request, 'user_center_site.html',context)

    def post(self,request):
        """编辑地址"""

        #接收编辑地址请求参数
        recv_name = request.POST.get('recv_name')
        addr = request.POST.get('addr')
        zip_code = request.POST.get('zip_code')
        recv_mobile = request.POST.get('recv_mobile')

        #校验参数
        if all([recv_name,addr,zip_code,recv_mobile]):
            #保存用户编辑的地址信息
            Address.objects.create(
                user = request.user,
                receiver_name = recv_name,
                receiver_mobile = recv_mobile,
                detail_addr = addr,
                zip_code = zip_code
            )

        #响应结果
        return redirect(reverse('users:address'))


    """
    用户认证系统进行判断用户是否登录
    方法一\传统方式（不好）：
        if request.user.is_authenticated():
            return render(request,'user_center_site.html')
        else:
            return redirect(reverse('users:login'))

    """


class LogoutView(View):
    """退出登录"""
    def get(self,request):
        """处理退出登录逻辑:
              需要知道清理谁的状态保持数据， # 由Django用户认证系统完成：需要清理cookie和session,request参数中有user对象
        """
        logout(request)

        return redirect(reverse('users:login'))

class LoginView(View):
    """登录"""
    def get(self,request):
        """提供登录页面"""
        return render(request,'login.html')
    def post(self,request):
        """处理登录逻辑"""
        #查询用户

        #接收用户登录表单数据
        user_name = request.POST.get('user_name')
        pwd = request.POST.get('pwd')

        #对用户进行验证，验证是否是该网站用户
        user = authenticate(username=user_name, password=pwd)

        #校验用户是否存在
        if user is None:
            return render(request,'login.html',{'errmsg':'用户名或密码错误'})

        #校验是否是激活用户
        if user.is_active is False:
            return render(request,'login.html',{'errmsg':'请激活'})

        # 使用django的用户认证系统，在session中保存用户的登陆状态
        #login()　默认将用户状态保持信息存入　django_session中
        #提示：如果我们选django_redis作为缓存的后端，并把session数据页存到redis中，就需要配置
        #SESSION_ENGINE SESSION_CACHE_ALTAS　我们login()会自动找session_engine
        login(request,user)

        #实现记住用户名／多少天免登录：如果用户勾选了记住密码，我们就把状态保持时间设置为十天，反之０秒
        remembered = request.POST.get('remembered')
        if remembered !='on':
            request.session.set_expiry(0) #设置状态保持０秒
        else:
            request.session.set_expiry(60*60*24*10)



        # 在登陆页面跳转前，将cookie和redis中的购物车数据合并到redis中
        cart_json = request.COOKIES.get('cart')
        if cart_json is not None:
            cart_dict_cookie = json.loads(cart_json)
        else:
            cart_dict_cookie = {}

        # 查询redis中购物车数据
        redis_conn = get_redis_connection('default')
        cart_dict_redis = redis_conn.hgetall('cart_%s'%user.id)

        # 遍历cookie中购物车数据sku_id　string类型　count　int类型
        for sku_id,count in cart_dict_cookie.items():

            sku_id = sku_id.encode() #将srt --->　bytes
            if sku_id in cart_dict_redis:
                #如果cookie中商品在redis中已经存在，叠加
                origin_count = cart_dict_redis[sku_id]
                #这里合并会造成购物车数据里面的数量超出某件商品的库存
                count += int(origin_count)

                # 库存超出的情况
                # sku = GoodsSKU.objects.get(id=sku_id)
                # if count > sku.stock:
                #     return '提示用户库存超出　伪代码'


            # 将ｃｏｏｋｉｅ中购物车数据加到redis中
            # 如果cookie中的商品在redis中有，就累加count赋值。反之，直接赋值cookie中的count
            cart_dict_redis[sku_id] = count

        # 将合并后的redis数据，设置到redis中:redis_conn.hmset()不能传入空字典
        if cart_dict_redis:
             redis_conn.hmset('cart_%s'%user.id,cart_dict_redis)



        #在登录成功，界面发送跳转之前，需要判断是跳转到主页还是next参数指向的一个页面
        next = request.GET.get('next')
        if next is None:
            #响应结果:跳转到主页
            response = redirect(reverse('goods:index'))
        else:
            if next == '/orders/place':
                response = redirect(reverse('cart:info'))
            else:
                #跳转到next指向的地址
                response = redirect(next)

        # 清除cookie
        response.delete_cookie('cart')

        return response


class ActiveView(View):
    """用户激活"""
    def get(self,request,token):
        """接收和处理激活请求"""
        #创建序列化器：参数必须要和调用dumps方法时候的参数相同
        serializer = Serializer(settings.SECRET_KEY,3600)

        #获取token,解出{'confirm':user_id}　需要在签名未过期时，读取结果
        try:
            result = serializer.loads(token)
        except SignatureExpired:
            return HttpResponse('激活连接已过期')

        #读取user_id :{'confirm':user_id｝
        user_id = result.get('confirm')

        #查询要激活的用户
        try:
            user = User.objects.get(id= user_id)
        except User.DoesNotExist:
            return HttpResponse('用户不存在')

        #重置激活状态为True
        user.is_active = True
        #需要手动保存
        user.save()

        #响应结果:跳转到登录页面
        return redirect(reverse('users:login'))

#View　是django自己定义的类视图的基类
class RegisterView(View):
    """注册：类视图"""

    #函数名是请求方法的小写
    def get(self,request):
        """提供注册页面"""
        return render(request, 'register.html')

    def post(self,request):
        """处理注册逻辑"""
        """
        获取
        校验
        保存
        """
        #接收注册请求参数
        user_name = request.POST.get('user_name')
        pwd = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        #校验注册请求参数
        #判断是否缺少参数：只要有一个为空，就返回假
        if not all([user_name,pwd,email]):
            #如果缺少参数，有问题就return，没有问题，就往下进行
            return redirect(reverse('users:register'))

        #判断邮箱格式
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
            return render(request,'register.html',{'errmsg':'邮箱格式错误'})

        #判断是否勾选了用户协议
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': '请勾选用户协议'})


        #保存注册请求参数
        # User.objects.create(
        #     #create()不用写save，
        #     #这样写密码没有加密,所以不这样写了，按照下面的django自带的用户认证系统方式写
        #     user_name = user_name,
        #     password = pwd,
        # )

        # 保存注册请求参数
        try:
            user = User.objects.create_user(user_name,email,pwd)
        except db.IntegrityError:
            #用户重名点击注册会提示IntegrityError错误，因为数据库中的username是默认设置唯一的
            return render(request,'register.html',{'errmsg':'用户已存在'})

        #重置激活状态
        user.is_active = False
        user.save()

        #生成口令token,用itsdangerous对user_id的混淆序列化
        token = user.generate_active_token()

        #异步发送激活邮件：不能够阻塞
        #触发异步任务 需要.delay()
        send_active_email.delay(email,user_name,token)



        #响应结果
        return redirect(reverse('goods:index'))


# def register(request):
#     """
#     注册：函数视图
#     如何在一个视图中，处理多种请求逻辑，前提是多种逻辑对应的请求地址相同，才能找到那个包含有多种逻辑的视图
#     根据不同的请求方法，进行请求逻辑的分发
#     """
#     """
#     一个视图中处理以下两种逻辑：
#         提供注册页面
#         处理注册逻辑
#     """
#     if request.method == 'GET':
#         """提供注册页面"""
#         return render(request,'register.html')
#     if request.method == 'POST':
#         """处理注册逻辑"""
#         return HttpResponse('这里是处理注册表单逻辑')






